module Main where

import Graphics.UI.Gtk
import Graphics.UI.Gtk.Glade
import GhCalcUtils
import ParseUtils
import Graphics.UI.Gtk.ModelView as ModelView
import Graphics.UI.Gtk.TreeList.TreeModel as TreeModel

import qualified Data.Tree as Tree

-- Currently we do not need the sophistication of a generic type
-- But when the need arises, we have to change this to be a polymorphic
data Command = Command String

getStringFromCommand :: Command -> String
getStringFromCommand (Command str) = str

convStringToCommand :: String-> Command
convStringToCommand str = Command str

main = do
    createLockFile
    initGUI
    gladeFilePath <- getGladeFilePath
    Just glade_xml <- xmlNew gladeFilePath
    setupHandlers glade_xml
    setupMenuHandlers glade_xml
    model <- setupRecentList glade_xml
    setupRecentListUpdater glade_xml model
    setupSaveHandler glade_xml model
    setupOpenHandler glade_xml model
    setupClearListHandler glade_xml model
    mainWindow <- xmlGetWidget glade_xml castToWindow "wGhcalc"
    onDestroy mainWindow mainQuit
    widgetShowAll mainWindow
    mainGUI

setupHandlers :: GladeXML -> IO ()
setupHandlers glade_xml = do
    setupDigitHandlers glade_xml
    setupOpHandlers glade_xml
    return ()

setupDigitHandlers :: GladeXML -> IO ()
setupDigitHandlers glade_xml = do
    formulaEntry <- xmlGetWidget glade_xml castToEntry "tFormula"
    digitButtons <- mapM (xmlGetWidget glade_xml castToButton) ["bDigit1", "bDigit2", "bDigit3", "bDigit4" ,"bDigit5" ,"bDigit6", "bDigit7" ,"bDigit8", "bDigit9", "bDigit0","bDot"]
    mapM (\button -> onClicked button (digitHandler formulaEntry button)) digitButtons
    return ()

setupOpHandlers :: GladeXML -> IO ()
setupOpHandlers   glade_xml = do
    formulaEntry <- xmlGetWidget glade_xml castToEntry "tFormula"
    opButtons <-
      mapM (xmlGetWidget glade_xml castToButton) ["bOpplus", "bOpminus", "bOpmul", "bOpdiv","bOpExp", "bOpPercent", "bOpenPara","bClosedPara"]
    mapM (\button -> onClicked button (digitHandler formulaEntry button)) opButtons

    -- evaluate expression on clicking of = and set the result entry
    bEnter <- xmlGetWidget glade_xml castToButton "bEnter"
    resultEntry <- xmlGetWidget glade_xml castToEntry "tResultEntry"
    onClicked bEnter (eqHandler formulaEntry resultEntry bEnter)

    --clear the text entries when Clear Button is clicked
    -- TODO set the focus to the formula entry
    bClear <- xmlGetWidget glade_xml castToButton "bClear"
    onClicked bClear (clearHandler formulaEntry resultEntry)

-- Handle backspace button
    bBackspace <- xmlGetWidget glade_xml castToButton "bBackspace"
    onClicked bBackspace (backspaceHandler formulaEntry)
    return ()

setupMenuHandlers :: GladeXML -> IO ()
setupMenuHandlers glade_xml = do
    -- set the quit handler,we may want to look for file to be saved
    quitMenuItem <- xmlGetWidget glade_xml castToMenuItem "fileQuitItem"
    quitMenuItem `onActivateLeaf` (on_quit_activate)

    -- set the clear recent list handler

    -- setting the about handler
    mainWindow <- xmlGetWidget glade_xml castToWindow "wGhcalc"
    helpAboutItem <-  xmlGetWidget glade_xml castToMenuItem "helpAboutItem"
    helpAboutItem `onActivateLeaf` (showAboutDialog mainWindow)
    return ()


digitHandler formulaEntry button = do
    digit <- buttonGetLabel button
    getSetTextEntry formulaEntry (\x -> x ++ digit)
    return ()

opHandler formulaEntry button = do
    op <- buttonGetLabel button
    getSetTextEntry formulaEntry (\x -> x ++ op)
    return ()

paraHandler formulaEntry button = do
  -- we can do realtime error
    para <- buttonGetLabel button
    getSetTextEntry formulaEntry (\x -> x ++ para)  				
    return ()

eqHandler formulaEntry resultEntry button = do
    evaluateAndSetResult formulaEntry resultEntry
    return()


clearHandler formulaEntry resultEntry = do
    clearTextEntries [formulaEntry, resultEntry]
    return ()

getSetTextEntry entry func = do
    entryText  <- entryGetText entry
    entrySetText entry (func entryText)

backspaceHandler formulaEntry = do
  getSetTextEntry formulaEntry (\x -> take (length x - 1) x)
  return ()

clearTextEntries entryList = do
    mapM (flip entrySetText "") entryList
    return ()

on_quit_activate = do
              mainQuit
              return ()

showAboutDialog parent = do
    aboutDialog <- aboutDialogNew
    set aboutDialog [
      aboutDialogName      := "GhCalc",
      aboutDialogVersion   := "0.1",
      aboutDialogCopyright := "Lakshmi Narasimhan Vaikuntam",
      aboutDialogComments  := "A calculator with expression editor",
      aboutDialogWebsite   := "http://code.google.com/p/ghcalc"
      ]
    windowSetTransientFor aboutDialog parent
    afterResponse aboutDialog $ \_ -> widgetDestroy aboutDialog
    widgetShow aboutDialog

setupRecentList :: GladeXML -> IO (ListStore Command)
setupRecentList glade_xml = do
    let commandList = []::([Command])
    store <- listStoreNew (commandList)
    view <- xmlGetWidget glade_xml castToTreeView "RecentList"

    treeViewSetModel view store
    treeViewSetHeadersVisible view True
    col1 <- ModelView.treeViewColumnNew
    treeViewColumnSetTitle col1 "Recent List"
    renderer1 <- ModelView.cellRendererTextNew
    cellLayoutPackStart col1 renderer1 True
    cellLayoutSetAttributes col1 renderer1 store $ \(Command str)  -> [ ModelView.cellText := str  ]
    treeViewInsertColumn view col1 0
    return store

setupRecentListUpdater :: GladeXML -> ListStore Command -> IO ()
setupRecentListUpdater glade_xml model = do

    -- When = is pressed we need to add an entry into the recent list --
    formulaEntry <- xmlGetWidget glade_xml castToEntry "tFormula"
    resultEntry <- xmlGetWidget glade_xml castToEntry "tResultEntry"
    view <- xmlGetWidget glade_xml castToTreeView "RecentList"
    eqButton <- xmlGetWidget glade_xml castToButton	"bEnter"
    onClicked eqButton (recentListUpdater formulaEntry model)

    -- When an entry in the list is pressed, we need to populate it in the formula entry
    onRowActivated view (\path col -> do
                                      (Just iter) <- ModelView.treeModelGetIter model
                                                                                path
                                      (Command str) <- ModelView.treeModelGetRow
                                                                model iter
                                      entrySetText formulaEntry str
                                      evaluateAndSetResult formulaEntry resultEntry
                                      return ())
    return ()

--recentListUpdater ::
recentListUpdater formulaEntry  model = do
    -- TODO check for duplicates --
    formulaText  <- entryGetText formulaEntry
    listStoreAppend model (Command formulaText)
    return ()

setupClearListHandler :: GladeXML -> ListStore Command -> IO ()
setupClearListHandler glade_xml model = do
  clearListItem <- xmlGetWidget glade_xml castToMenuItem "editClearList"
  onActivateLeaf clearListItem $ do
    listStoreClear model
  return ()

setupSaveHandler :: GladeXML -> ListStore Command -> IO()
setupSaveHandler glade_xml model = do
    saveItem <- xmlGetWidget glade_xml castToMenuItem "fileSaveItem"
    onActivateLeaf saveItem $ do
        maybeIter <- ModelView.treeModelGetIterFirst model
        let checkIter commands Nothing = return commands
            checkIter commands (Just cIter) = do
                command  <- ModelView.treeModelGetRow model cIter
                mIter <- ModelView.treeModelIterNext model cIter
                checkIter (commands ++ (command:[])) mIter
        commandList <- checkIter [] maybeIter
        sessionFileName <- getSessionFileName
        saveContents commandList sessionFileName
    return ()
    -- Get the List data from the model --

setupOpenHandler :: GladeXML -> ListStore Command -> IO ()
setupOpenHandler glade_xml  model = do
    openItem <- xmlGetWidget glade_xml castToMenuItem "fileOpenItem"
    onActivateLeaf openItem $ do
	sessionFileName <- getSessionFileName
        commandList <- openContents sessionFileName
        mapM (listStoreAppend model) commandList
        return ()
    return ()

saveContents :: [Command] -> FilePath -> IO ()
saveContents commandList fileName = do
    let strList =   map getStringFromCommand commandList
    let strToWrite = concat (map (\s -> s ++ "\n") strList)
    writeFile fileName strToWrite
    return ()

openContents :: FilePath -> IO [Command]
openContents fileName = do
    contents <- readFile fileName
    let strList = lines contents
    let commandList = map convStringToCommand strList
    return commandList

--evaluateAndSetResult ::
evaluateAndSetResult formulaEntry resultEntry = do
    formula_text  <- entryGetText formulaEntry
    maybe_postfix_formula <- (run expr formula_text)
    case maybe_postfix_formula of
             (Just postfix_formula)-> entrySetText  resultEntry (num_to_str (evaluate_postfix postfix_formula))
             (Nothing) -> entrySetText  resultEntry "0"

